// Unit test for util::thread interface
// Copyright 2005 (C) Ralph Thomas

#include <tests/testCore.h>
#include <util/thread.h>
#include <util/mutex.h>
#include <util/condition.h>
#include <time.h>
#include <iostream>

#ifdef _WINDOWS
#include <windows.h>
#define sleep( x ) Sleep( (x) * 1000 )
#else
#include <unistd.h>
#endif

namespace util {
	//
	/// This little worker has a priority and sleeps for one second. It
	/// then writes the time at which it completes into some time_t
	/// pointer it was given.
	//
	class sleepyWorker : public thread::work {
		priority _p;
		time_t* _time;
	  public:
		sleepyWorker( priority p, time_t* pTime ) : _p( p ),
		 _time( pTime ) {}
		virtual priority getPriority() const { return _p; }
		virtual void doWork() {
			sleep( 1 );
			time( _time );
		}
	};
	//
	/// This unit test exercises basic properties of the thread interface.
	//
	class threadTest : public test::basic {
	  public:
		const char* performTest() {
			thread mine;
			time_t first = 0, second = 0, third = 0;
			//
			// We make a couple of waiter workers, and check that
			// they run in the correct order.
			//
			sleepyWorker* firstWorker = new sleepyWorker(
				thread::work::kWorkPriorityRegular, &first );
			sleepyWorker* thirdWorker = new sleepyWorker(
				thread::work::kWorkPriorityRegular, &third );
			sleepyWorker* secondWorker = new sleepyWorker(
				thread::work::kWorkPriorityUser, &second );
			//
			// Make the thread start executing these work items.
			// Note that we make the thread adopt the "second"
			// worker third, but that it has a higher priority.
			// This should mean that it gets run second.
			//
			mine.adoptWork( firstWorker );
			mine.adoptWork( thirdWorker );
			mine.adoptWork( secondWorker );
			//
			// Now we sleep for 4 seconds, until the thread has
			// finished it's work. Yes, this is really horrible
			// synchronization, but this is also just a test.
			//
			sleep( 4 );
			//
			// We can check that the second work was performed
			// before the third thread.
			//
			if( second > third )
				return "second worker ran after third";
			//
			// Well, that all seemed to work.
			//
			return 0;
		}
		const char* getTestName() {
			return "exercising basic properties of util::thread";
		}
	};
	//
	/// This worker waits for a condition and then increments a value.
	/// It is used to test that only one thread is woken up at a time.
	//
	class conditionalWorker : public thread::work {
		static condition _cond;
		static mutex _countLock;
		static unsigned int _count;
	  public:
		virtual priority getPriority() const {
			return kWorkPriorityRegular;
		}
		virtual void doWork() {
			_cond.wait();
			_countLock.lock();
			_count++;
			_countLock.unlock();
		}
		static unsigned int getCount() {
			_countLock.lock();
			unsigned int count = _count;
			_countLock.unlock();
			return count;
		}
		static void signal() { _cond.signal(); }
	};
	unsigned int conditionalWorker::_count = 0;
	mutex conditionalWorker::_countLock;
	condition conditionalWorker::_cond;
	//
	/// This test exercises the condition wrapper by having several
	/// threads wait on the condition and checking that they don't
	/// all get woken at once.
	//
	class conditionTest : public test::basic {
	  public:
		const char* performTest() {
			thread* t[3];
			conditionalWorker* w[3];
			//
			// Check that the count starts out at zero.
			//
			if( conditionalWorker::getCount() != 0 )
				return "the condition worker had incorrect initial count";
			//
			// Create the conditional workers.
			//
			for( unsigned int i = 0; i < 3; i++ ) {
				t[i] = new thread;
				t[i]->adoptWork( new conditionalWorker() );
			}
			//
			// Ensure that none of the threads have executed
			// already.
			//
			if( conditionalWorker::getCount() != 0 )
				return "the condition didn't hold at all";
			//
			// Try to increase the count.
			//
			conditionalWorker::signal();
			if( conditionalWorker::getCount() > 1 )
				return "the signal woke multiple threads 1";
			if( conditionalWorker::getCount() == 0 )
				return "the signal didn't wake any threads 1";
			//
			// Again...
			//
			conditionalWorker::signal();
sleep( 0 );
			if( conditionalWorker::getCount() > 2 )
				return "the signal woke multiple threads 2";
			if( conditionalWorker::getCount() == 1 )
				return "the signal didn't wake any threads 2";
			//
			// And again.
			//
			conditionalWorker::signal();
			if( conditionalWorker::getCount() > 3 )
				return "the signal woke multiple threads 3!!!";
			if( conditionalWorker::getCount() == 2 )
				return "the signal didn't wake any threads 3";
			//
			// delete threads
			//
			for( unsigned int i = 0; i < 3; i++ ) { delete t[i]; }
			return 0;
		}
		const char* getTestName() {
			return "testing some attributes of util::condition";
		}
	};
};

void test::runnerSingleton::registerTests() {
	//adoptTest( new util::threadTest );
	adoptTest( new util::conditionTest );
}

